﻿using FlyingCircusWebIS.Filtros;
using FlyingCircusWebIS.Models;
using FlyingCircusWebIS.ViewModels.ReservaVM;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PagedList;
using System.Web.UI.DataVisualization.Charting;
using System.IO;
using System.Drawing;
using FlyingCircusWebIS.Controllers.Utilidades;

namespace FlyingCircusWebIS.Controllers
{
    public class ReservaController : Controller
    {
        private IReservaService reservaService;
        private IVooService vooService;
        private IAeronaveService aeronaveService;
        private IFormaPagamentoService formaPagamentoService;
        private IPessoaService pessoaService;
        private ICupomService cupomService;

        public ReservaController()
        {
            reservaService = new ReservaService();
            vooService = new VooService();
            aeronaveService = new AeronaveService();
            formaPagamentoService = new FormaPagamentoService();
            pessoaService = new PessoaService();
            cupomService = new CupomService();
        }


        [AutenticacaoFilter("A")]
        public ActionResult Index()
        {
            return RedirectToAction("DashBoard");
        }

        [AutenticacaoFilter("A")]
        public ActionResult Dashboard()
        {
            ReservaDashboardVM vm = new ReservaDashboardVM();

            return View(vm);
        }

        /* 
         * Lista o raking de cidades de maior interesse como |destino| pelos compradores 
         * 
         */
        [AutenticacaoFilter("A")]
        public ActionResult ListarRankingCidadesReservas()
        {
            var dadosRanking = reservaService.GerarRelacaoDeCidadesDestinoRaking(5);
            return PartialView(dadosRanking);
        }

        [AutenticacaoFilter("A")]
        public ActionResult GraficoReservasPorFormasPagamento()
        {
            // Recupera os dados para geração do gráfico
            var dadosGrafico = reservaService.GerarModelGraficoFormasPagamento();

            Chart chart = new Chart();
            chart.Width = 350;
            chart.Height = 350;

            ChartArea chartArea = new ChartArea();
            chart.ChartAreas.Add(chartArea);

            Series series = new Series("Dados");
            series.ChartType = SeriesChartType.Pie;

            
            // Faz o bind dos dados recuperados e as séries especificadas
            series.Points.DataBindXY(dadosGrafico, "Descricao",
                dadosGrafico, "Quantidade");

            // Formatação de font, label, legendas etc
            series.Label = "#VALX: #PERCENT";
            series["PieLabelStyle"] = "Inside";
            series.Font = new Font("Verdana", 10f, FontStyle.Regular);

            chart.Series.Add(series);

            // Processo para retornar o gráfico como imagem
            MemoryStream ms = new MemoryStream();
            chart.SaveImage(ms, ChartImageFormat.Png);

            return File(ms.ToArray(), "image/png");
        }

        [AutenticacaoFilter("A")]
        public ActionResult GraficoReservasFaturamento()
        {
            // Recupera os dados para geração do gráfico
            var dadosGrafico = reservaService.GerarModelGraficoFaturamento();

            Chart chart = new Chart();
            chart.Width = 920;
            chart.Height = 350;

            ChartArea chartArea = new ChartArea();
            chart.ChartAreas.Add(chartArea);

            Series series = new Series("Dados");
            series.ChartType = SeriesChartType.Column;

            // Faz o bind dos dados recuperados e as séries especificadas
            series.Points.DataBindXY(dadosGrafico, "Descricao",
                dadosGrafico, "Quantidade");

            // Formatação de font, label, legendas etc
            series.Font = new Font("Verdana", 9f, FontStyle.Regular);
            
            chart.Series.Add(series);

            // Processo para retornar o gráfico como imagem
            MemoryStream ms = new MemoryStream();
            chart.SaveImage(ms, ChartImageFormat.Png);

            return File(ms.ToArray(), "image/png");
        }

        /**
         * Exibe a relação de reservas canceladas.
         */
        [AutenticacaoFilter("A")]
        public ActionResult Auditoria(int pagina = 1, string nomeCliente = "")
        {
            var logs = reservaService.ListarLogsReservaPorTipoModificacao();
            logs = logs.FindAll(e =>
                e.PessoaCliente.NomeCompleto.StartsWith(nomeCliente, StringComparison.InvariantCultureIgnoreCase));

            ViewBag.NomeCliente = nomeCliente;
            return View(logs.ToPagedList(pagina, 15));
        }

        
        public ActionResult Procurar(int pagina = 1, string cidadeOrigem = "", string cidadeDestino = "", string dataInicial = "", string dataFinal = "")
        {
            var voosEncontrados = vooService.Filtrar(cidadeOrigem, cidadeDestino, dataInicial, dataFinal).ToPagedList(pagina, 8);


            return View(new ReservaProcurarVM
            {
                Registros = voosEncontrados,
                TotalRegistros = voosEncontrados.Count,
                CidadeOrigem = cidadeOrigem,
                CidadeDestino = cidadeDestino,
                DataInicial = dataInicial,
                DataFinal = dataFinal
            });
        }

        [AutenticacaoFilter]
        public ActionResult Reservar(int id)
        {
            ReservarVM vm = new ReservarVM();

            vm.AssentosOcupados = reservaService.ListarReservasDoVoo(id).Select(a => a.NumAssento).ToList();
            vm.ItensFormaDePagamento = formaPagamentoService.ListarItensAtivosFormaDePagamento();

            vm.voo = vooService.BuscarPorId(id);

            vm.IdVoo = id;
            vm.Assentos = vm.voo.AssentosComercializaveis;
            vm.IdModeloAeronave = vm.voo.Aeronave.IdModeloAeronave;
            vm.TotalAssentosPorModelo = vm.voo.Aeronave.ModeloAeronave.Assentos;
            TimeSpan totalHoras = vm.voo.DataChegada.Subtract(vm.voo.DataPartida);
            vm.TempoDeVoo = totalHoras.Hours.ToString("00") + "h" + totalHoras.Minutes.ToString("00") + "min";

            Decimal valorReservaCompleto = (vm.voo.PrecoVoo * (1 + vm.voo.Lucro) / vm.Assentos);
            vm.PrecoReserva = valorReservaCompleto.ToString("c");
            
            vm.DescontoDeVooPromocional = vm.voo.VooPromocional == "S" ? (- valorReservaCompleto* vm.voo.DescontoPromocao).Value.ToString("c") : (Decimal.Zero).ToString("c");

            //TODO corrigir sessão
            vm.IdPessoaCliente = (Pessoa)Session["usuario"] != null ? ((Pessoa)Session["usuario"]).IdPessoa : 0;
            if ((Pessoa)Session["usuario"] != null )
            {
                Pessoa cliente = pessoaService.BuscarPessoaPorId(vm.IdPessoaCliente);
                vm.DescontoClienteEspecial = cliente.DescontoEspecial == "S" ? (-valorReservaCompleto *(1 - vm.voo.DescontoPromocao)*(cliente.PorcDescontoEspecial)).Value.ToString("c") : (0).ToString("c");
            }
            else
            {
                vm.DescontoClienteEspecial = (Decimal.Zero).ToString("c");
            }
            


            //apresenta o numero de assentos por linha de acordo com o IdModeloAeronave
            if (vm.IdModeloAeronave == 1)
                vm.AssentosPorLinha = 6;
            else if (vm.IdModeloAeronave == 2)
                vm.AssentosPorLinha = 7;
            else
                vm.AssentosPorLinha = 10;

            //Dado a quantidade total de assentos e o total de assentos por linha, cria uma matriz que representa os assentos            
            vm.Matriz = CriarMatrizAssentos(vm.TotalAssentosPorModelo, vm.AssentosPorLinha);

            return View(vm);
        }

        [AutenticacaoFilter]
        [HttpPost]
        public ActionResult Reservar(ReservarVM vm)
        {
            // Recebe os dados do processo de reserva

            if (ModelState.IsValid)
            {
                Reserva registro = ConverterReservarVM(vm);
                List<ValidationResult> erros = reservaService.ValidarEntidade(registro);

                if (erros.Count == 0)
                {
                    bool sucesso = false;
                    if (vm.Edicao)
                        sucesso = reservaService.Atualizar(registro);
                    else
                        sucesso = reservaService.Adicionar(registro);

                    if(sucesso)
                        return RedirectToAction("Sucesso",
                            new { titulo = "Sucesso!", mensagem = "Sua reserva foi efetuada com sucesso!" });
                    else
                        return RedirectToAction("Sucesso",
                            new { titulo = "Erro!", mensagem = "Não foi possivel realizar a reserva. Tente novamente em alguns minutos." });
                }
                else
                {
                    ModelState.AddModelErrors(erros);
                }
            }

            PopularReservarVM(vm);

            return View(vm);
        }

        [AutenticacaoFilter]
        public ActionResult Sucesso(string titulo, string mensagem)
        {
            @ViewBag.Titulo = titulo;
            @ViewBag.Mensagem = mensagem;

            return View();
        }

        [AutenticacaoFilter]
        [HttpPost]
        public ActionResult CancelarReserva(int id)
        {
            bool cancelou = reservaService.CancelarReservaPorId(id);

            JsonActionResultModel jarm = new JsonActionResultModel();
            if (cancelou)
            {
                jarm.Erro = false;
                jarm.Mensagem = "Reserva cancelada com sucesso.\n Em alguns minutos você receberá um e-mail com detalhes.";
            }
            else
            {
                jarm.Erro = true;
                jarm.Mensagem = "Não é possível deletar a reserva. Tente novamente em alguns minutos.";
            }


            return Json(jarm);
        }

        // -----------------------------------------------------------------------------------------
        // Métodos auxiliares 
        // -----------------------------------------------------------------------------------------

        private void PopularReservarVM(ReservarVM vm)
        {
            vm.AssentosOcupados = reservaService.ListarReservasDoVoo(vm.IdVoo).Select(a => a.NumAssento).ToList();
            vm.ItensFormaDePagamento = formaPagamentoService.ListarItensAtivosFormaDePagamento();
            vm.Matriz = CriarMatrizAssentos(vm.TotalAssentosPorModelo, vm.AssentosPorLinha);
        }

        private List<List<int>> CriarMatrizAssentos(int qtdAssentos, int colunas)
        {
            List<List<int>> Matriz = new List<List<int>>();

            int i = 0;
            while (Matriz.Count < QuantidadeDeLinhas(qtdAssentos, colunas))
            {
                List<int> linha = new List<int>();

                while (linha.Count < colunas && i < qtdAssentos)
                {
                    linha.Add(i + 1);
                    i++;
                }
                Matriz.Add(linha);
            }

            return Matriz;
        }

        //Retorna o número de linhas de uma matriz dinâmica, dado o numero total de elementos e o número de colunas
        private int QuantidadeDeLinhas(int total, int colunas)
        {
            return total % colunas == 0 ? total / colunas : total / colunas + 1;
        }

        private Reserva ConverterReservarVM(ReservarVM vm)
        {
            Reserva entidade = new Reserva();

            entidade.NumAssento = vm.AssentoSelecionado != null ? int.Parse(vm.AssentoSelecionado) : 0;
            entidade.DataVenda = DateTime.Now;
            entidade.IdPessoaCliente = vm.IdPessoaCliente;
            entidade.PessoaCliente = pessoaService.BuscarPessoaPorId(entidade.IdPessoaCliente);
            entidade.IdVoo = vm.IdVoo;
            entidade.CodCupom = vm.CodCupom;
            entidade.IdPagamento = vm.IdPagamento != null ? int.Parse(vm.IdPagamento) : 0;
            entidade.FormaPagamento = formaPagamentoService.BuscarPorId(entidade.IdPagamento);
            entidade.Voo = vooService.BuscarPorId(vm.IdVoo);
            entidade.PrecoReserva = ValorDaReserva(entidade.IdVoo, entidade.IdPessoaCliente, entidade.CodCupom);

            return entidade;
        }

        public JsonResult ApresentaValorReserva(int idVoo, int idCliente, string codCupom)
        {
            Decimal valor = reservaService.CalcularValorDaReserva(idVoo, idCliente, codCupom);
            return Json(valor.ToString("c"), JsonRequestBehavior.AllowGet);
        }

        private Decimal ValorDaReserva(int idVoo, int idCliente, string codCupom)
        {
            Decimal valor = reservaService.CalcularValorDaReserva(idVoo, idCliente, codCupom);
            return valor;
        }

        public JsonResult RetornaCupom(string codCupom)
        {
            string DescontoCupom = Decimal.Zero.ToString("P");
            Cupom c = cupomService.BuscarPorCodCupomAtivo(codCupom);
            
            if(c != null)
                DescontoCupom = cupomService.BuscarPorCodCupomAtivo(codCupom).Desconto.ToString("P");

            return Json(DescontoCupom, JsonRequestBehavior.AllowGet);
        }

        public JsonResult ApresentaAssentoSelecionado(int assentoSelecionado, int idModeloAeronave)
        {
            string assentoFormatado = reservaService.ApresentaAssentoSelecionado(assentoSelecionado, idModeloAeronave);

            return Json(assentoFormatado, JsonRequestBehavior.AllowGet);
        }

    }
}
